﻿using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using Microsoft.TeamFoundation.WorkItemTracking.Client;
using ScrumPilot.Domain.Models;
using ScrumPilot.TFSHelpers;
using Project = ScrumPilot.Domain.Models.Project;

namespace ScrumPilot.Domain.BPL
{
    public class ProjectServices : ProjectAndKanbanServices
    {
        public ProjectServices(TFSServices tfs) : base(tfs) { }

        public IEnumerable<project> GetProjects()
        {
            IEnumerable<project> projects;
            using (var unitOfWork = new UnitOfWork())
            {
                projects = unitOfWork.ProjectRepository.GetAll();
            }
            return projects;
        }

        public bool AddProject(project project)
        {
            using (var unitOfWork = new UnitOfWork())
            {
                unitOfWork.ProjectRepository.Add(project);
                return unitOfWork.Save() > 0;
            }
        }

        public bool UpdateProject(project prj, int id)
        {
            using (var unitOfWork = new UnitOfWork())
            {
                unitOfWork.ProjectRepository.Update(prj);
                return unitOfWork.Save() > 0;
            }
        }

        public bool DeleteProject(int id)
        {
            using (var unitOfWork = new UnitOfWork())
            {
                project project = unitOfWork.ProjectRepository.GetById(id);
                unitOfWork.ProjectRepository.Delete(project);
                return unitOfWork.Save() > 0;
            }
        }

        public List<project> GetProjectPaged(int page, int rows, out int totalCount)
        {
            List<project> projects;
            using (var unitOfWork = new UnitOfWork())
            {
                projects = unitOfWork.ProjectRepository.GetProjectPaged(page, rows, out totalCount);
            }
            return projects;
        }

        public project GetById(int id)
        {
            project project;
            using (var unitOfWork = new UnitOfWork())
            {
                project = unitOfWork.ProjectRepository.GetById(id);
            }
            return project;
        }

        public project GetByName(String name)
        {
            project project;
            using (var unitOfWork = new UnitOfWork())
            {
                project = unitOfWork.ProjectRepository.Get(p => p.name.Equals(name));
            }
            return project;
        }

        public List<Project> GetAllProjects()
        {
            List<project> lstProjects = CheckIfProjectsExist();
            var lstRet = new List<Project>(lstProjects.Count);

            lstProjects.ForEach(pj =>
            {
                var pr = GetProject(pj);
                if (pr != null)
                    lstRet.Add(pr);
            });

            return lstRet;
        }

        public List<Project> GetProjectsFiltered(int[] selected)
        {
            List<Project> lstProjects = this.GetAllProjects();
            var sl = new List<int>(selected);
            foreach (var project in lstProjects)
            {
                project.Selected = sl.Contains(project.ID);
            }

            return lstProjects;
        }

        public Project GetProject(Int32 projectID)
        {
            project pj = this.GetById(projectID);
            return GetProject(pj);
        }

        public List<Iteration> GetSprints()
        {
            var lRet = new List<Iteration> { new Iteration { ID = "-1", Name = "Choose a project/Sprint" } };
            using (var unitOfWork = new UnitOfWork())
            {
                unitOfWork.ProjectRepository.Filter(p => !p.notuse).ToList().ForEach(c =>
                                                                                               {
                                                                                                   IEnumerable<Iteration> lstI =
                                                                                                       RecurseNodeSprint(
                                                                                                           TFS.GetProject(c.projectid).IterationRootNodes);
                                                                                                   lRet.AddRange(lstI);
                                                                                               }
                                    );
            }
            return lRet;
        }

        public List<Iteration> GetAreas(String sprintPath)
        {
            var lRet = new List<Iteration> { new Iteration { ID = "-1", Name = "Choose an area/module" } };
            if (!String.IsNullOrEmpty(sprintPath))
            {
                // if there is a sprint choosen
                string projectName = sprintPath.Contains("\\") ? sprintPath.Substring(0, sprintPath.IndexOf('\\')) : sprintPath;
                lRet.AddRange(
                    RecurseNodeArea(
                        TFS.GetProject(this.GetByName(projectName).projectid).AreaRootNodes));
            }
            return lRet;
        }

        public int GetAreaID(String areaPath)
        {
            int iRet = -1;
            if (!String.IsNullOrEmpty(areaPath))
            {
                List<Iteration> lSearch = GetAreas(areaPath);
                iRet = int.Parse(lSearch.Find(p => p.Name == areaPath).ID);
            }
            return iRet;
        }


     
        public static String IterationName(NodeCollection nodes, int id)
        {
            foreach (Node nd in nodes)
            {
                if (nd.IsIterationNode && nd.Id == id)
                    return nd.Name;

                if (nd.HasChildNodes)
                {
                    var ret = IterationName(nd.ChildNodes, id);
                    if (ret != null)
                        return ret;
                }
            }
            return null;
        }

        private static IEnumerable<Iteration> RecurseNodeArea(NodeCollection nodes)
        {
            var lstRet = new List<Iteration>();
            foreach (Node nd in nodes)
            {
                if (nd.IsAreaNode)
                    lstRet.Add(new Iteration { ID = nd.Id.ToString(CultureInfo.InvariantCulture), Name = nd.Path });

                if (nd.HasChildNodes) // Recurse in subNodes
                    lstRet.AddRange(RecurseNodeArea(nd.ChildNodes));
            }
            return lstRet;
        }

        /// <summary>
        /// Check if projects in TFS exist in Database and, if not, create it
        /// </summary>
        /// <returns>Database list of projects</returns>
        private List<project> CheckIfProjectsExist()
        {
            List<project> lstProjects = this.GetProjects().ToList();
            List<int> projectNotExist = (from p in TFS.GetSIDProjects()
                                         where !(from o in lstProjects
                                                 select o.projectid).Contains(p)
                                         select p).ToList();

            if (projectNotExist.Count > 0)
            {
                // Check if project exist, if no, create it in DB
                foreach (int iPrj in projectNotExist)
                {
                    Microsoft.TeamFoundation.WorkItemTracking.Client.Project pj = TFS.GetProject(iPrj);
                    this.AddProject(new project
                    {
                        projectid = iPrj,
                        name = pj.Name,
                        notuse = true,
                        currentsprint = ""
                    });
                }
                lstProjects = this.GetProjects().ToList(); // Reload list 
            }
            return lstProjects;
        }

        public Boolean UpdateProject(Project model)
        {
            using (var unitOfWork = new UnitOfWork())
            {
                project prj = unitOfWork.ProjectRepository.GetById(model.ID);
                prj.notuse = !model.Selected;
                prj.currentsprint = model.CurrentSprint;
                model.Name = prj.name;
                return unitOfWork.Save() > 0;
            }
        }

        public void SubscribeEvents(int projectID, Boolean typeC, Boolean typeW)
        {
            using (var unitOfWork = new UnitOfWork())
            {
                project prj = unitOfWork.ProjectRepository.GetById(projectID);
                tfsserverparam tfsparams = unitOfWork.TfsServerParamsRepository.GetOne();
                if (prj.subscribecheckin.HasValue != typeC) // Checkin Event
                {
                    if (typeC) // Subscribe
                    {
                        if (!String.IsNullOrWhiteSpace(tfsparams.spwsaddress))
                        {
                            prj.tokencheckin = Guid.NewGuid();
                            prj.subscribecheckin = TFS.SubscribeEvent(EventTypes.CheckinEvent, tfsparams.spwsaddress,
                                                                      prj.tokencheckin.Value.ToString());
                            if (prj.subscribecheckin.Value >= 0)
                                unitOfWork.Save();
                        }
                    }
                    else // Unsubscribe
                    {
                        TFS.UnSubscribeEvent(prj.subscribecheckin.Value);
                        prj.subscribecheckin = null;
                        prj.tokencheckin = null;
                        unitOfWork.Save();
                    }
                }
                if (prj.subscribewichanged.HasValue != typeW) // WorkItemchanged event
                {
                    if (typeW) // Subscribe
                    {
                        if (!String.IsNullOrWhiteSpace(tfsparams.spwsaddress))
                        {
                            prj.tokenwichanged = Guid.NewGuid();
                            prj.subscribewichanged = TFS.SubscribeEvent(EventTypes.WorkItemChangedEvent,
                                                                        tfsparams.spwsaddress,
                                                                        prj.tokenwichanged.Value.ToString());
                            if (prj.subscribewichanged.Value >= 0)
                                unitOfWork.Save();
                        }
                    }
                    else // Unsubscribe
                    {
                        TFS.UnSubscribeEvent(prj.subscribewichanged.Value);
                        prj.subscribewichanged = null;
                        prj.tokenwichanged = null;
                        unitOfWork.Save();
                    }
                }
            }
        }

    }
}
